package cn.iocoder.yudao.module.system.service.dept;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.core.SysConstants;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.common.util.tree.TreeUtil;
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptTreeVO;
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.UserDeptDO;
import cn.iocoder.yudao.module.system.dal.mysql.dept.DeptMapper;
import cn.iocoder.yudao.module.system.dal.mysql.user.UserDeptMapper;
import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
import cn.iocoder.yudao.module.system.service.businessliaison.BusinessLiaisonService;
import cn.iocoder.yudao.module.system.service.tenant.TenantService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;

/**
 * 部门 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Validated
@Slf4j
public class DeptServiceImpl implements DeptService {

    @Resource
    private DeptMapper deptMapper;
    @Resource
    @Lazy //延迟加载
    private TenantService tenantService;
    @Resource
    @Lazy
    private AdminUserService adminUserService;
    @Resource
    private UserDeptMapper userDeptMapper;
    @Resource
    @Lazy
    private BusinessLiaisonService businessLiaisonService;

    @Override
    @CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST,
            allEntries = true) // allEntries 清空所有缓存，因为操作一个部门，涉及到多个缓存
    public Long createDept(DeptSaveReqVO createReqVO) {
        if (createReqVO.getParentId() == null) {
            createReqVO.setParentId(DeptDO.PARENT_ID_ROOT);
        }
        // 校验父部门的有效性
        validateParentDept(null, createReqVO.getParentId());
        // 校验部门名的唯一性
        validateDeptNameUnique(null, createReqVO.getParentId(), createReqVO.getName());

        // 插入部门
        DeptDO dept = BeanUtils.toBean(createReqVO, DeptDO.class);
        if (dept.getTenantId() == null) {
            dept.setTenantId(TenantContextHolder.getTenantId());
        }
        deptMapper.insert(dept);
        return dept.getId();
    }

    @Override
    @CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST,
            allEntries = true) // allEntries 清空所有缓存，因为操作一个部门，涉及到多个缓存
    public void updateDept(DeptSaveReqVO updateReqVO) {
        if (updateReqVO.getParentId() == null) {
            updateReqVO.setParentId(DeptDO.PARENT_ID_ROOT);
        }
        // 校验自己存在
        validateDeptExists(updateReqVO.getId());
        // 校验父部门的有效性
        validateParentDept(updateReqVO.getId(), updateReqVO.getParentId());
        // 校验部门名的唯一性
        validateDeptNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName());

        // 更新部门
        DeptDO updateObj = BeanUtils.toBean(updateReqVO, DeptDO.class);
        deptMapper.updateById(updateObj);
    }

    @Override
    @CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST,
            allEntries = true) // allEntries 清空所有缓存，因为操作一个部门，涉及到多个缓存
    public void deleteDept(Long id) {
        // 校验是否存在
        validateDeptExists(id);
        // 校验是否有子部门
        if (deptMapper.selectCountByParentId(id) > 0) {
            throw exception(DEPT_EXITS_CHILDREN);
        }
        // 删除部门
        deptMapper.deleteById(id);
    }

    @VisibleForTesting
    void validateDeptExists(Long id) {
        if (id == null) {
            return;
        }
        DeptDO dept = deptMapper.selectById(id);
        if (dept == null) {
            throw exception(DEPT_NOT_FOUND);
        }
    }

    @VisibleForTesting
    void validateParentDept(Long id, Long parentId) {
        if (parentId == null || DeptDO.PARENT_ID_ROOT.equals(parentId)) {
            return;
        }
        // 1. 不能设置自己为父部门
        if (Objects.equals(id, parentId)) {
            throw exception(DEPT_PARENT_ERROR);
        }
        // 2. 父部门不存在
        DeptDO parentDept = deptMapper.selectById(parentId);
        if (parentDept == null) {
            throw exception(DEPT_PARENT_NOT_EXITS);
        }
        // 3. 递归校验父部门，如果父部门是自己的子部门，则报错，避免形成环路
        if (id == null) { // id 为空，说明新增，不需要考虑环路
            return;
        }
        for (int i = 0; i < Short.MAX_VALUE; i++) {
            // 3.1 校验环路
            parentId = parentDept.getParentId();
            if (Objects.equals(id, parentId)) {
                throw exception(DEPT_PARENT_IS_CHILD);
            }
            // 3.2 继续递归下一级父部门
            if (parentId == null || DeptDO.PARENT_ID_ROOT.equals(parentId)) {
                break;
            }
            parentDept = deptMapper.selectById(parentId);
            if (parentDept == null) {
                break;
            }
        }
    }

    @VisibleForTesting
    void validateDeptNameUnique(Long id, Long parentId, String name) {
        DeptDO dept = deptMapper.selectByParentIdAndName(parentId, name);
        if (dept == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的部门
        if (id == null) {
            throw exception(DEPT_NAME_DUPLICATE);
        }
        if (ObjectUtil.notEqual(dept.getId(), id)) {
            throw exception(DEPT_NAME_DUPLICATE);
        }
    }

    @Override
    public DeptDO getDept(Long id) {
        return deptMapper.selectById(id);
    }

    @Override
    public List<DeptDO> getDeptList(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return Collections.emptyList();
        }
        return deptMapper.selectBatchIds(ids);
    }

    @Override
    public List<DeptDO> getDeptList(DeptListReqVO reqVO) {

        if (reqVO.getTenantId() == null) {
            reqVO.setTenantId(TenantContextHolder.getTenantId());
        } else {
            if (!tenantService.isExistSubTenant(reqVO.getTenantId())) {
                throw exception(TENANT_AUTH_ERROR);
            }

        }
        List<DeptDO> list = deptMapper.selectList(reqVO);
        list.sort(Comparator.comparing(DeptDO::getSort));
        return list;
    }

    /**
     * 获得租户集合下所有部门信息数组
     *
     * @return 部门信息数组
     */
    @Override
    public List<DeptDO> getAllDeptByTenantList(List<Long> tenantIdList) {
        LambdaQueryWrapper<DeptDO> queryWrapper = new LambdaQueryWrapperX<DeptDO>()
                .eqIfPresent(DeptDO::getStatus, CommonStatusEnum.ENABLE.getStatus())
                .inIfPresent(DeptDO::getTenantId, tenantIdList);

        List<DeptDO> list = deptMapper.selectList(queryWrapper);
        list.sort(Comparator.comparing(DeptDO::getSort));
        return list;
    }

    /**
     * 获得部门信息数组
     *
     * @return 部门信息数组
     */
    @Override
    public List<DeptDO> getAllDeptList() {
        List<TenantDO> tenantList = tenantService.getSubTenant();
        List<Long> tenantIds = tenantList.stream().map(TenantDO::getId).collect(Collectors.toList());

        if (CollectionUtils.isEmpty(tenantIds)) {
            throw exception(TENANT_EMPTY_ERROR);
        }
        List<DeptDO> list = deptMapper.selectList(new LambdaQueryWrapperX<DeptDO>()
                .eqIfPresent(DeptDO::getStatus, CommonStatusEnum.ENABLE.getStatus())
                .inIfPresent(DeptDO::getTenantId, tenantIds));

        list.sort(Comparator.comparing(DeptDO::getSort));
        return list;
    }


    @Override
    public List<DeptDO> getChildDeptList(Collection<Long> ids) {
        List<DeptDO> children = new LinkedList<>();
        // 遍历每一层
        Collection<Long> parentIds = ids;
        for (int i = 0; i < Short.MAX_VALUE; i++) { // 使用 Short.MAX_VALUE 避免 bug 场景下，存在死循环
            // 查询当前层，所有的子部门
            List<DeptDO> depts = deptMapper.selectListByParentId(parentIds);
            // 1. 如果没有子部门，则结束遍历
            if (CollUtil.isEmpty(depts)) {
                break;
            }
            // 2. 如果有子部门，继续遍历
            children.addAll(depts);
            parentIds = convertSet(depts, DeptDO::getId);
        }
        return children;
    }

    @Override
    public List<DeptDO> getChildDeptByParentId(Long deptId, List<DeptDO> list) {
        List<DeptDO> deptDOS = deptMapper.selectList(DeptDO::getParentId, deptId);
        list.addAll(deptDOS);
        for (DeptDO dept : deptDOS) {
            getChildDeptByParentId(dept.getId(), list);
        }
        return list;
    }

    @Override
    public List<DeptDO> getDeptListByLeaderUserId(Long id) {
        return deptMapper.selectListByLeaderUserId(id);
    }

    @Override
    @DataPermission(enable = false) // 禁用数据权限，避免建立不正确的缓存
    @Cacheable(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, key = "#id")
    public Set<Long> getChildDeptIdListFromCache(Long id) {
        List<DeptDO> children = getChildDeptList(id);
        return convertSet(children, DeptDO::getId);
    }

    @Override
    public void validateDeptList(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return;
        }
        // 获得科室信息
        Map<Long, DeptDO> deptMap = getDeptMap(ids);
        // 校验
        ids.forEach(id -> {
            DeptDO dept = deptMap.get(id);
            if (dept == null) {
                throw exception(DEPT_NOT_FOUND);
            }
            if (!CommonStatusEnum.ENABLE.getStatus().equals(dept.getStatus())) {
                throw exception(DEPT_NOT_ENABLE, dept.getName());
            }
        });
    }

    /**
     * 获得用户上级领导
     *
     * @param deptId 部门id
     * @return 部门领导
     */
    @Override
    public Long getLeaderId(Long deptId) {
        Long leaderId;

        DeptDO deptDO = deptMapper.selectById(deptId);
        /*if (deptDO.getLeaderUserId().equals(userId)) {
            // 本人，则获得分管领导
            Long respLeaderUserId = deptDO.getRespLeaderUserId();
            if (respLeaderUserId == null) {
                // 分管领导为空，则获得上级部门信息
                LambdaQueryWrapper<DeptDO> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(DeptDO::getId, deptDO.getParentId());
                DeptDO parent = deptMapper.selectOne(queryWrapper);

                leaderId = parent.getLeaderUserId();
            } else {
                leaderId = respLeaderUserId;
            }
        } else {
            leaderId = deptDO.getLeaderUserId();
        }*/
        if (deptDO.getLeaderUserId() == null) {
            throw exception(DEPT_LEADER_NOT_FOUND, deptId);
        }

        return deptDO.getLeaderUserId();
    }

    /**
     * 获得部门上级领导
     *
     * @param deptId 部门id
     * @return 上级领导
     */
    @Override
    public Long getParentDeptLeaderId(Long deptId, Long userId) {

        DeptDO deptDO = deptMapper.selectById(deptId);
        if (deptDO.getParentId() == 0) {
//            若部门是部门根节点，则是公司负责人
            return deptDO.getLeaderUserId();
        }

        LambdaQueryWrapper<DeptDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DeptDO::getId, deptDO.getParentId());

        DeptDO parentDept = deptMapper.selectOne(queryWrapper);
        if (userId == null || parentDept.getParentId() == 0) {
            return parentDept.getLeaderUserId();
        }
        Long leaderId;
        if (parentDept.getLeaderUserId().equals(userId)) {
            // 本人，则获得分管领导
            Long respLeaderUserId = parentDept.getRespLeaderUserId();
            if (respLeaderUserId == null) {
                // 分管领导为空，则获得上级部门信息
                LambdaQueryWrapper<DeptDO> queryWrapper2 = new LambdaQueryWrapper<>();
                queryWrapper2.eq(DeptDO::getId, parentDept.getParentId());
                DeptDO parent = deptMapper.selectOne(queryWrapper2);

                leaderId = parent.getLeaderUserId();
            } else {
                leaderId = respLeaderUserId;
            }
        } else {
            leaderId = parentDept.getLeaderUserId();
        }

        return leaderId;
    }

    /**
     * 根据用户，获得有权限的用户集合
     *
     * @return
     */
    @Override
    public List<Long> getDeptOfUserIds() {
        // 根据用户，获得有权限的用户集合
        // 普通员工获得自己，部门领导，获得部门下所有员工


        Long userId = WebFrameworkUtils.getLoginUserId();
        // 若是部门领导，获取部门内若有的
        // 其它获得自己

        // 若是分公司领导，应该获得公司所有员工，对应租户的所有员工
        MPJLambdaWrapperX<DeptDO> query = new MPJLambdaWrapperX<>();
        query.eq(DeptDO::getLeaderUserId, userId);
        query.eq(DeptDO::getParentId, DeptDO.PARENT_ID_ROOT);
        query.ne(DeptDO::getTenantId, SysConstants.TOP_TENANT_ID);// 非集团
        List<DeptDO> deptDOList = deptMapper.selectList(query);
        if (!CollectionUtils.isEmpty(deptDOList)) {
            // 说明是分公司最高领导，则获得公司下所有员工
            List<AdminUserDO> userList = adminUserService.getUserListByTenant();
            return userList.stream().map(AdminUserDO::getId).collect(Collectors.toList());
        }

        MPJLambdaWrapperX<DeptDO> queryWrapper = new MPJLambdaWrapperX<>();
        queryWrapper.eq(DeptDO::getLeaderUserId, userId);
        List<DeptDO> deptDOS = deptMapper.selectList(queryWrapper);
        if (!CollectionUtils.isEmpty(deptDOS)) {
            List<Long> resList = new ArrayList<>();
            for (DeptDO deptDO : deptDOS) {
                MPJLambdaWrapperX<UserDeptDO> queryWrapper2 = new MPJLambdaWrapperX<>();
                queryWrapper2.distinct();
                queryWrapper2.select(UserDeptDO::getUserId);
                queryWrapper2.eq(UserDeptDO::getDeptId, deptDO.getId());
                queryWrapper2.isNotNull(UserDeptDO::getUserId);
                List<UserDeptDO> userDeptDOList = userDeptMapper.selectList(queryWrapper2);
                if (!CollectionUtils.isEmpty(userDeptDOList)) {
                    List<Long> userIds = userDeptDOList.stream().map(UserDeptDO::getUserId).collect(Collectors.toList());
                    resList.addAll(userIds);
                }
            }
            // 有的领导不在部门中，需要单独加上
            resList.add(userId);
            return resList;
        }


        return Collections.singletonList(userId);
    }

    /**
     * 获得租户下顶级部门信息
     *
     * @param tenantId 租户id
     * @return 公司负责人
     */
    public DeptDO getCompanyTopDept(Long tenantId) {
        MPJLambdaWrapper<DeptDO> queryWrapper = new MPJLambdaWrapper<>();
        queryWrapper.selectAll(DeptDO.class)
                .eq(DeptDO::getTenantId, tenantId)
                .eq(DeptDO::getParentId, DeptDO.PARENT_ID_ROOT);

        return deptMapper.selectOne(queryWrapper);
    }

    /**
     * 获得所有顶级部门信息
     *
     * @return 部门信息
     */
    private List<DeptDO> getAllTopDept() {
        return deptMapper.selectList(new LambdaQueryWrapper<DeptDO>()
                .eq(DeptDO::getParentId, DeptDO.PARENT_ID_ROOT));
    }

    /**
     * 获得部门树
     * <p>
     * 智能办公
     *
     * @return 部门树
     */
    @Override
    public List<DeptTreeVO> getDeptTreeForOf() {
        List<DeptTreeVO> resList = new ArrayList<>();
        // 租户id
        Long tenantId = TenantContextHolder.getRequiredTenantId();
        // 如果租户是123，需要拿平级的数据
        if (tenantId.equals(SysConstants.TOP_TENANT_ID)) {
            // 获得所有顶级节点部门
            List<DeptDO> allTopDept = getAllTopDept();
            DeptDO selfDept = allTopDept.stream().filter(deptDO -> tenantId.equals(deptDO.getTenantId())).findFirst().orElse(new DeptDO());

            // 获得自己部门信息
            List<DeptDO> allDept = getAllDeptByTenantList(Collections.singletonList(tenantId));
            List<DeptTreeVO> deptList = allDept.stream().map(this::buildDeptTreeVO).collect(Collectors.toList());
            DeptTreeVO selfDeptTree = TreeUtil.buildTreeContainRoot(deptList, DeptTreeVO::getId, DeptTreeVO::getParentId, DeptTreeVO::getSort, DeptTreeVO::setChildren, selfDept.getId());

            // 获得其它部门下顶级节点
            List<DeptTreeVO> otherDept = allTopDept.stream().filter(deptDO -> !deptDO.getTenantId().equals(tenantId))
                    .sorted(Comparator.comparing(DeptDO::getSort))
                    .map(this::buildDeptTreeVO).collect(Collectors.toList());

            resList.add(selfDeptTree);
            resList.addAll(otherDept);
        } else {
            // 分公司，获得自己的即可
            List<DeptDO> allDept = getAllDeptByTenantList(Collections.singletonList(tenantId));
            // 自己顶级的
            DeptDO rootDept = allDept.stream().filter(deptDO -> deptDO.getParentId().equals(DeptDO.PARENT_ID_ROOT)).findFirst().orElse(new DeptDO());

            resList = allDept.stream().map(this::buildDeptTreeVO).collect(Collectors.toList());
            DeptTreeVO deptTreeVO = TreeUtil.buildTreeContainRoot(resList, DeptTreeVO::getId, DeptTreeVO::getParentId, DeptTreeVO::getSort, DeptTreeVO::setChildren, rootDept.getId());
            resList.clear();
            resList.add(deptTreeVO);
        }

        return resList;
    }

    /**
     * 获得当前用户的部门信息
     *
     * @return
     */
    public DeptTreeVO getUserDeptVO(Long userId) {
        // 先看用户是否是部门负责人，是的话，则返回部门负责人信息
        LambdaQueryWrapper<DeptDO> queryWrapper = new LambdaQueryWrapperX<DeptDO>()
                .eq(DeptDO::getStatus, CommonStatusEnum.ENABLE.getStatus())
                .eq(DeptDO::getLeaderUserId, userId);
        List<DeptDO> deptDO = deptMapper.selectList(queryWrapper);
        if(!CollectionUtils.isEmpty(deptDO)){
            return buildDeptTreeVO(deptDO.get(0));
        }

        List<UserDeptDO> userDeptDOS = userDeptMapper.selectList(UserDeptDO::getUserId, userId);
        if (CollectionUtils.isEmpty(userDeptDOS)) {
            throw exception(DEPT_USER_NOT_FOUND, userId);
        }
        if (userDeptDOS.size() > 1) {
            throw exception(DEPT_USER_FOUND_MORE, userId);
        }

        return buildDeptTreeVO(deptMapper.selectById(userDeptDOS.get(0).getDeptId()));
    }

    /**
     * 构建部门树元素
     *
     * @param deptDO 部门
     * @return 部门树
     */
    private DeptTreeVO buildDeptTreeVO(DeptDO deptDO) {
        DeptTreeVO deptTree = new DeptTreeVO();
        deptTree.setId(deptDO.getId());
        deptTree.setName(deptDO.getName());
        deptTree.setParentId(deptDO.getParentId());
        deptTree.setSort(deptDO.getSort());
        deptTree.setTenantId(deptDO.getTenantId());
        deptTree.setLeaderUserId(deptDO.getLeaderUserId());
        // 接口人
        AdminUserDO userDO = adminUserService.getUser(deptDO.getLeaderUserId());
        if (userDO != null) {
            deptTree.setLeaderUserName(userDO.getNickname());
            deptTree.setShowName(userDO.getNickname()); //部门领导
            Long businessLiaison = businessLiaisonService.getBusinessLiaison(1, null, deptTree.getId(), deptDO.getLeaderUserId());

            if (!businessLiaison.equals(deptDO.getLeaderUserId())) {
                userDO = adminUserService.getUser(businessLiaison);
                deptTree.setDistributionUserId(userDO.getId());
                deptTree.setDistributionUserName(userDO.getNickname());
                deptTree.setShowName(userDO.getNickname());// 子公司的，显示接口人
            }
        }

        return deptTree;
    }


}
